package com.ptl.bp.controller;

import com.ptl.bp.config.ApiResult;
import com.ptl.bp.config.BizException;
import com.ptl.bp.util.FileUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.util.unit.DataSize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Objects;

@Tag(name = "文件上传下载")
@Slf4j
@RestController
@RequestMapping("/api/file")
public class FileController {

    @Value("${app.upload.max-file-size:20MB}")
    private DataSize maxFileSize;

    @Value("${app.upload.host:127.0.0.1}")
    private String uploadHost;

    @Value("${server.port}")
    private Integer uploadPort;

    @Value("${app.upload.protocol}")
    private String uploadProtocol;

    @Value("${app.upload.dir}")
    private String uploadPath;

    @Operation(summary = "上传文件")
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ApiResult<String> upload(@RequestPart("file") MultipartFile file) throws IOException {
        log.info("上传文件: 原始文件名={}, 大小={}字节", file.getOriginalFilename(), file.getSize());
        // 文件大小校验
        if (file.getSize() > maxFileSize.toBytes()) {
            return ApiResult.fail(String.format("文件大小超过限制(最大%s)", maxFileSize));
        }
        // 确保上传目录存在
        Path uploadDir = Paths.get(uploadPath);
        if (!Files.isDirectory(uploadDir)) {
            Files.createDirectories(uploadDir);
        }
        // 生成新文件名并保存
        String newName = FileUtils.generateRandomFileName(file.getOriginalFilename());
        Path destination = uploadDir.resolve(newName);

        try (InputStream inputStream = file.getInputStream()) {
            Files.copy(inputStream, destination, StandardCopyOption.REPLACE_EXISTING);
        }
        // file.transferTo(destination.toFile()); // 相对路径报错; 使用transferTo而不是Files.write，避免内存问题

        log.info("文件上传成功: 存储路径={}", destination);
        return ApiResult.success(String.format("%s://%s:%s/api/file/get/%s", uploadProtocol, uploadHost,
                Objects.equals(uploadProtocol, "https") ? 443 : uploadPort, newName));
    }

    @Operation(summary = "获取文件(支持预览或下载)")
    @GetMapping("/get/{filename:.+}")
    public void getFile(@PathVariable("filename") String filename,
                        @RequestParam(required = false, defaultValue = "false") boolean download,
                        HttpServletResponse response) throws IOException {
        log.info("获取文件: {}, 模式: {}", filename, download ? "下载" : "预览");
        if (filename.contains("..")) {
            throw new SecurityException("非法文件名");
        }
        Path filePath = Paths.get(uploadPath, filename);
        if (!Files.exists(filePath)) {
            log.warn("文件不存在: {}", filePath);
            throw new BizException("文件不存在!");
        }

        // 设置Content-Type
        response.setHeader("Cache-Control", "public, max-age=2592000");
        response.setContentType(MediaTypeFactory.getMediaType(filename).orElse(MediaType.APPLICATION_OCTET_STREAM).toString());
        // 根据模式设置响应头
        if (download) {
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
        } else {
            // 对于预览模式，可以添加一些安全相关的头
            response.setHeader("Content-Disposition", "inline");
            response.setHeader("X-Content-Type-Options", "nosniff");
        }

        response.setContentLengthLong(Files.size(filePath));
        Files.copy(filePath, response.getOutputStream());
    }
}